package lib.homhomlib.design;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DragInfo;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.StackLayout;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

/**
 * 下拉上拉布局
 */
public class SlidingLayout extends StackLayout implements ComponentContainer.LayoutRefreshedListener,
        ComponentContainer.BindStateChangedListener {
    /**
     * 同时可以上拉和下拉
     */
    public static final int SLIDING_MODE_BOTH = 0;

    /**
     * 只能头部往下拉
     */
    public static final int SLIDING_MODE_TOP = 1;

    /**
     * 只能底部往上拉
     */
    public static final int SLIDING_MODE_BOTTOM = 2;

    /**
     * 状态正在拉
     */
    public static final int STATE_SLIDING = 2;

    /**
     * 结束拉
     */
    public static final int STATE_IDLE = 1;


    private static final int SLIDING_DISTANCE_UNDEFINED = -1;
    private static final String BACKGROUND_COMPONENT = "background_component";
    private static final String SLIDING_MODE = "sliding_mode";
    private static final String TOP_MAX = "top_max";
    private static final int RESET_DURATION = 200;
    private static final int SMOOTH_DURATION = 1000;

    // 系统允许最小的滑动判断值
    private int mTouchSlop;
    private int mBackgroundViewLayoutId = 0;

    // 背景Component
    private Component mBackgroundComponent;

    // 正面Component
    private Component mTargetComponent;

    // 滑动阻力系数
    private float mSlidingOffset = 0.5F;
    private int mSlidingMode = SLIDING_MODE_BOTH;
    private int mSlidingTopMaxDistance = SLIDING_DISTANCE_UNDEFINED;
    private SlidingListener mSlidingListener;
    private DraggedListener mDelegateDraggedListener;

    /**
     * 监听事件
     */
    public interface SlidingListener {
        // 不能操作繁重的任务在这里
        void onSlidingOffset(Component component, float delta);

        void onSlidingStateChange(Component component, int state);

    }

    public SlidingLayout(Context context) {
        this(context, null);
    }

    public SlidingLayout(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    public SlidingLayout(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }


    private void init(Context context, AttrSet attrs) {
        boolean hasAttr;
        hasAttr = attrs.getAttr(BACKGROUND_COMPONENT).isPresent();
        if (hasAttr) {
            String layoutId = attrs.getAttr(BACKGROUND_COMPONENT).get().getStringValue();
            if (layoutId != null && layoutId.contains("$layout:")) {
                layoutId = layoutId.replace("$layout:", "");
                try {
                    mBackgroundViewLayoutId = Integer.parseInt(layoutId);
                } catch (NumberFormatException e) {
                    HiLog.error(new HiLogLabel(HiLog.ERROR, 0xD003A01, "LocalHiLog"),
                            "background component layoutId error");
                }
            }
        }
        hasAttr = attrs.getAttr(SLIDING_MODE).isPresent();
        if (hasAttr) {
            mSlidingMode = attrs.getAttr(SLIDING_MODE).get().getIntegerValue();
        }
        hasAttr = attrs.getAttr(TOP_MAX).isPresent();
        if (hasAttr) {
            mSlidingTopMaxDistance = attrs.getAttr(TOP_MAX).get().getDimensionValue();
        }
        if (mBackgroundViewLayoutId != 0) {
            Component component = LayoutScatter.getInstance(context).parse(mBackgroundViewLayoutId, null, false);
            setBackgroundComponent(component);
        }
        mTouchSlop = 30;
        setDraggedListener(1, draggedListener);
        setLayoutRefreshedListener(this);
        setBindStateChangedListener(this);
    }

    /**
     * 设置背景组件
     *
     * @param component 背景组件
     */
    public void setBackgroundComponent(Component component) {
        if (mBackgroundComponent != null) {
            this.removeComponent(mBackgroundComponent);
        }
        mBackgroundComponent = component;
        LayoutConfig layoutConfig = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
        addComponent(component, 0, layoutConfig);
    }

    /**
     * 获取背景组件
     *
     * @return 组件
     */
    public Component getBackgroundComponent() {
        return this.mBackgroundComponent;
    }

    /**
     * 设置最大可滑动的距离
     *
     * @param distance 距离
     */
    public void setSlidingDistance(int distance) {
        this.mSlidingTopMaxDistance = distance;
    }

    /**
     * 返回最大可滑动的距离
     *
     * @return 距离
     */
    public int getSlidingDistance() {
        return this.mSlidingTopMaxDistance;
    }

    /**
     * 获得滑动幅度
     *
     * @return 滑动幅度
     */
    public float getSlidingOffset() {
        return this.mSlidingOffset;
    }

    /**
     * 设置滑动幅度
     *
     * @param slidingOffset 滑动幅度
     */
    public void setSlidingOffset(float slidingOffset) {
        this.mSlidingOffset = slidingOffset;
    }

    /**
     * 设置监听事件
     *
     * @param slidingListener 接口
     */
    public void setSlidingListener(SlidingListener slidingListener) {
        this.mSlidingListener = slidingListener;
    }


    @Override
    public void onRefreshed(Component component) {
        if (getChildCount() == 0) {
            return;
        }
        if (mTargetComponent == null) {
            ensureTarget();
        }
        if (mTargetComponent == null) {
            return;
        }
    }


    private void ensureTarget() {
        if (mTargetComponent == null) {
            mTargetComponent = getComponentAt(getChildCount() - 1);
        }
    }

    /**
     * 设置正面组件
     *
     * @param view 正面组件
     */
    public void setTargetView(Component view) {
        if (mTargetComponent != null) {
            this.removeComponent(mTargetComponent);
        }
        mTargetComponent = view;
        this.addComponent(view);
    }

    /**
     * 设置滑动事件
     *
     * @param draggedListener 滑动接口
     */
    public void setDraggedListener(DraggedListener draggedListener) {
        mDelegateDraggedListener = draggedListener;
    }

    /**
     * 获取正面Component
     *
     * @return 正面组件
     */
    public Component getTargetComponent() {
        return this.mTargetComponent;
    }

    /**
     * 获取滑动的偏移量
     *
     * @return 正面组件的偏移量
     */
    public float getSlidingTranslationY() {
        return getTargetComponent().getTranslationY();
    }

    private Instrument getInstrument() {
        return Instrument.getInstance();
    }

    private DraggedListener draggedListener = new DraggedListener() {
        int startY = 1;

        @Override
        public void onDragDown(Component component, DragInfo dragInfo) {
            if (mDelegateDraggedListener != null) {
                mDelegateDraggedListener.onDragDown(component, dragInfo);
            }
        }

        @Override
        public void onDragStart(Component component, DragInfo dragInfo) {
            if (mDelegateDraggedListener != null) {
                mDelegateDraggedListener.onDragStart(component, dragInfo);
            }
            startY = dragInfo.startPoint.getPointYToInt();
        }

        @Override
        public void onDragUpdate(Component component, DragInfo dragInfo) {
            if (mDelegateDraggedListener != null) {
                mDelegateDraggedListener.onDragUpdate(component, dragInfo);
            }
            int updateY = dragInfo.updatePoint.getPointYToInt();
            onSliding(component, startY, updateY);
        }

        @Override
        public void onDragEnd(Component component, DragInfo dragInfo) {
            if (mDelegateDraggedListener != null) {
                mDelegateDraggedListener.onDragEnd(component, dragInfo);
            }
            float translationY = mTargetComponent.getTranslationY();
            getInstrument().reset(mTargetComponent, translationY, RESET_DURATION);
            if (mSlidingListener != null) {
                mSlidingListener.onSlidingStateChange(component, STATE_IDLE);
            }
        }

        @Override
        public void onDragCancel(Component component, DragInfo dragInfo) {
            if (mDelegateDraggedListener != null) {
                mDelegateDraggedListener.onDragCancel(component, dragInfo);
            }
        }
    };

    private void onSliding(Component component, int startY, int updateY) {
        float delta = updateY - startY;
        if (Math.abs(delta) > mTouchSlop) {
            float tempOffset = 1 - Math.abs(mTargetComponent.getTranslationY()
                    / mTargetComponent.getHeight());
            delta = delta * mSlidingOffset * tempOffset;
            switch (mSlidingMode) {
                case SLIDING_MODE_BOTH:
                    getInstrument().slidingByDelta(mTargetComponent, delta);
                    break;
                case SLIDING_MODE_TOP:
                    if (delta > 0) {
                        if (mSlidingTopMaxDistance != SLIDING_DISTANCE_UNDEFINED
                                && delta > mSlidingTopMaxDistance) {
                            // 超过滑动范围
                            delta = mSlidingTopMaxDistance;
                        }
                        getInstrument().slidingByDelta(mTargetComponent, delta);
                    }
                    break;
                case SLIDING_MODE_BOTTOM:
                    if (delta < 0) {
                        getInstrument().slidingByDelta(mTargetComponent, delta);
                    }
                    break;
            }
            if (mSlidingListener != null) {
                mSlidingListener.onSlidingStateChange(component, STATE_SLIDING);
                mSlidingListener.onSlidingOffset(component, delta);
            }
        }
    }

    /**
     * 设置滑动方式
     *
     * @param mode 0为双向都可以滑动 1为顶部滑动 2为底部滑动
     */
    public void setSlidingMode(int mode) {
        mSlidingMode = mode;
    }

    /**
     * 获取滑动模式
     *
     * @return 滑动模式
     */
    public int getSlidingMode() {
        return mSlidingMode;
    }

    /**
     * 滑动动画
     *
     * @param translationY 偏移量Y
     */
    public void smoothScrollTo(float translationY) {
        getInstrument().smoothTo(mTargetComponent, translationY, SMOOTH_DURATION);
    }


    @Override
    public void onComponentBoundToWindow(Component component) {
    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        getInstrument().clearAnimation();
        mSlidingMode = 0;
        mTargetComponent = null;
        mBackgroundComponent = null;
        mSlidingListener = null;
    }


}
